#include "main.h"
#include "dma.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
#include "flash.h"
#include "stm32f4xx_it.h"
#include <string.h>
#include "update.h"
#include "flash.h"
#define POLYNOMIAL 0x1021  
  
#define INITIAL_CRC 0xFFFF 

#define ONE_SECTOR_SIZE (128*1024)
#define TOTAL_SECTOR    7

extern DMA_HandleTypeDef hdma_usart2_tx;
extern DMA_HandleTypeDef hdma_usart2_rx;
extern UART_HandleTypeDef huart2;


typedef enum 
{
  CMD_NONE = 0x00,
	HAND_SHAKE = 0x48,
	UPDATE_START= 0x49,
	UPDATE_PROCEED= 0x4A,
	UPDATE_FINISH= 0x4B,
	UPDATE_APP_RESULT_FEEDBACK= 0x4C,	
}UPDATE_STAGE_t;

typedef enum 
{
	ERROR_NONE             = 0x00,
	ERROR_FLASH_ERROR            ,
	ERROR_FLASH_TIMEOUT          ,
	ERROR_FILE_SIZE_EXCEED       ,
	ERROR_CRC_FAILED             ,
}UPDATE_ERROR_t;

typedef enum
{
	BUTTON_ACTIVE = 0X00,
	BUTTON_RELEASE = 0x01,
}BUTTON_t;
UPDATEFILE_t update_app;
UPDATEFILE_t update_app_read;
uint8_t receive_data[512];
uint8_t read_app_data[1024];
uint8_t package_reply[10] = {0x23,0xA5,0x00,0x05,0x01,0x01,0,0,0xA7,0xA8};	//������ȫ�ֱ�������Ϊ�жϹ��̻�ı�ֲ�������
uint8_t package_update_reply[12] = {0x23,0xA5,0x00,0x05,0x01,0x00,0x00,0x01,0,0,0xA7,0xA8};	
uint8_t shake_hand_reply[9] = {0xA3,0xA6,HAND_SHAKE,0x01,0x02,0,0,0xA8,0xA9};
uint32_t crc32(uint32_t crc, uint8_t *buff, uint32_t len);

void init_updatefile(void)
{
	update_app.file_receive_finish = 0;
	update_app.update_result = WAIT_FOR_UPDATE;
	update_app.one_package_size = 256;	
	update_app.file_read_finish_R = 0;

}
BUTTON_t get_button_state(void)
{
		static uint8_t sum = 5,i = 0;
		static uint8_t filter[5] = {1,1,1,1,1};	
		static BUTTON_t button_state;
		uint8_t key_state = HAL_GPIO_ReadPin(key0_GPIO_Port,key0_Pin);
		sum = sum + key_state - filter[i%5];
		filter[i%5] = key_state;
		i++;
		if(sum == 5)
		{
			button_state = BUTTON_RELEASE;
		}	
		else if(sum == 0)
		{
			button_state = BUTTON_ACTIVE;
		}	
		return button_state;	
}

ACTION_t power_button_check(void)
{
    BUTTON_t power_button_state;	
	  static BUTTON_t power_button_state_last = BUTTON_RELEASE;
	  static uint16_t button_keep_time = 0;
	  static uint8_t time_start_flag = 0,time_stop_flag = 0;
	  static ACTION_t action = ACTION_NONE;
    power_button_state = get_button_state();
	  
	  if(power_button_state == BUTTON_ACTIVE && power_button_state_last == BUTTON_RELEASE)
		{
			  time_start_flag = 1;	
		}

	  if(power_button_state == BUTTON_RELEASE && power_button_state_last == BUTTON_ACTIVE && time_start_flag == 1)
		{
			  time_stop_flag = 1;	
		}		
		if(time_start_flag)
		{
			 button_keep_time++;
		}
		if(time_start_flag)
		{
			if(time_stop_flag && button_keep_time < 100)
			{
				  action = ACTION_JUMPTOAPP;
			}
			if(button_keep_time > 500)
			{
				  action = ACTION_WAIT_FOR_UPDATE;			
			}				
		}
	  power_button_state_last = power_button_state;
		
		return action;	
}
uint16_t crc16(unsigned char *data, uint32_t len,uint16_t initial_crc)
{
  uint16_t crc = initial_crc;  
  for (uint32_t i = 0; i < len; i++)
	{  
    crc ^= (uint16_t)(data[i] << 8); 
    for (uint16_t j = 0; j < 8; j++) 
		{  
				if (crc & 0x8000) 
				{ 
						crc = (crc << 1) ^ POLYNOMIAL; 
				} 
				else
				{  
						crc = crc << 1;
				}  
				crc &= 0xFFFF;
     }  
    }  
    return crc;  
}
UPDATE_ERROR_t erase_flash_by_app_size(UPDATEFILE_t *m_updat_app)
{
	TE_FLASH_STATUS flash_status;
	m_updat_app->total_sectors = m_updat_app->file_size/ONE_SECTOR_SIZE + 1*(m_updat_app->file_size%ONE_SECTOR_SIZE != 0);
  if(m_updat_app->total_sectors > (TOTAL_SECTOR - 1))
	{
		return ERROR_FILE_SIZE_EXCEED;
	}
	else
  {
		if(m_updat_app->total_sectors == 6)
		{
			  flash_status = FlashErase(E_FLASH_SECTOR_5,7);
		}
		else
    {
			  flash_status = FlashErase(E_FLASH_SECTOR_5,m_updat_app->total_sectors);
		    flash_status = FlashErase(E_FLASH_SECTOR_11,1);
		}
		return (UPDATE_ERROR_t)flash_status;			
	}
}

void write_update_packkage(uint8_t *p_receive_data)
{
	update_app.update_index = (p_receive_data[5] << 8) | p_receive_data[6];
	if(update_app.update_index < update_app.total_package)
	{
	    FlashWrite((FLASH_APP_ADDR+update_app.update_index*update_app.one_package_size),&p_receive_data[7],update_app.one_package_size);		
	}
}
void cmd_send_read_flash_result(uint8_t cmd,UPDATE_ERROR_t error_num)
{
		uint16_t data_len,crc;	

		data_len = sizeof(package_reply) -6;	
		package_reply[2] = (data_len >> 8) & 0xFF;	
		package_reply[3] = data_len & 0xFF;		
		package_reply[4] = cmd;
		package_reply[5] = error_num;
		crc = crc16((unsigned char*)&package_reply[2],data_len,INITIAL_CRC);
		package_reply[data_len+2] = (crc >> 8)&0xFF;
		package_reply[data_len+3] = (crc)&0xFF;
		HAL_UART_Transmit_DMA(&huart2,(uint8_t *)package_reply,sizeof(package_reply));			
}
void cmd_package_reply(uint8_t cmd,UPDATE_ERROR_t error_num)
{
		uint16_t data_len,crc;		
    if(cmd == UPDATE_PROCEED)		
		{
				data_len = sizeof(package_update_reply) -6;	
				package_update_reply[2] = (data_len >> 8) & 0xFF;	
				package_update_reply[3] = data_len & 0xFF;		
				package_update_reply[4] = cmd;
				package_update_reply[5] = (update_app.update_index >> 8) & 0xFF;			
				package_update_reply[6] = update_app.update_index & 0xFF;			
				package_update_reply[7] = error_num;
				crc = crc16((unsigned char*)&package_update_reply[2],data_len,INITIAL_CRC);
				package_update_reply[data_len+2] = (crc >> 8)&0xFF;
				package_update_reply[data_len+3] = (crc)&0xFF;
				HAL_UART_Transmit_DMA(&huart2,(uint8_t *)package_update_reply,sizeof(package_update_reply));			
		}
		else
		{
				data_len = sizeof(package_reply) -6;	
				package_reply[2] = (data_len >> 8) & 0xFF;	
				package_reply[3] = data_len & 0xFF;		
				package_reply[4] = cmd;
				package_reply[5] = error_num;
				crc = crc16((unsigned char*)&package_reply[2],data_len,INITIAL_CRC);
				package_reply[data_len+2] = (crc >> 8)&0xFF;
				package_reply[data_len+3] = (crc)&0xFF;
				HAL_UART_Transmit_DMA(&huart2,(uint8_t *)package_reply,sizeof(package_reply));			
		}
	
}
void cmd_shakehand_reply(void)
{
		uint16_t crc;
		crc = crc16((unsigned char*)&shake_hand_reply[4],1,INITIAL_CRC);
		package_reply[5] = (crc >> 8)&0xFF;
		package_reply[6] = (crc)&0xFF;
		HAL_UART_Transmit_DMA(&huart2,(uint8_t *)shake_hand_reply,sizeof(shake_hand_reply));	
}
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
	uint16_t data_len = 0;
	uint16_t crc,cmd = 0xFF;
	UPDATE_ERROR_t error;
	if(huart == &huart2)
	{
		if(receive_data[0] == 0xA3 && receive_data[1] == 0xA6)
		{
			cmd = receive_data[2];
			if(cmd == HAND_SHAKE)
			{
				cmd_shakehand_reply();
			}
		}
		if(receive_data[0] == 0x23 && receive_data[1] == 0xA5)
		{
			data_len = (receive_data[2] << 8) | receive_data[3];
			cmd = receive_data[4];
			crc = crc16((unsigned char*)&receive_data[2],data_len,INITIAL_CRC);
			if(crc == ((receive_data[data_len+2] << 8) | receive_data[data_len+3]))
			{				
					switch(cmd)
					{
						case UPDATE_START:
							update_app.file_size =(uint32_t)((receive_data[5] << 24) | (receive_data[6] << 16)
						                         | (receive_data[7] << 8)|  receive_data[8]);
						
						  update_app.file_crc = (receive_data[9] << 8)|  receive_data[10];
						  update_app.total_package = update_app.file_size/update_app.one_package_size 
						                             + 1*(update_app.file_size%update_app.one_package_size != 0);
						  init_updatefile();
						  error = erase_flash_by_app_size(&update_app);
							cmd_package_reply(cmd,ERROR_NONE);
							break;
						case UPDATE_PROCEED:
							write_update_packkage(receive_data);
						  cmd_package_reply(cmd,ERROR_NONE);
							break;
						case UPDATE_FINISH:
							cmd_package_reply(cmd,ERROR_NONE);
						  update_app.file_receive_finish = 1;
							break;
						case UPDATE_APP_RESULT_FEEDBACK:		
              update_app.file_read_finish_R = 1;							
							break;
						default:
							break;
					}	
         memset(receive_data,0,sizeof(receive_data));			
			}
			else
      {
					cmd_package_reply(cmd,ERROR_CRC_FAILED);	
			}

		}			
	}

}
void Send_Read_Flash_App_Result(void)
{
	uint16_t read_times = 0,once_read_size = 1024,crc_current;
	static uint16_t crc_last = INITIAL_CRC,i = 0,file_crc_right_flag = 0;
    UPDATE_ERROR_t error;
	if(update_app.file_receive_finish && update_app.update_result == WAIT_FOR_UPDATE)
	{
		read_times = update_app.file_size/once_read_size + 1*(update_app.file_size%once_read_size != 0);
		for(i = 0; i < read_times; i++)
		{

				if(update_app.file_size%once_read_size != 0 && (i == read_times - 1))
				{
						error = (UPDATE_ERROR_t)FlashRead(FLASH_APP_ADDR + i*once_read_size,read_app_data,update_app.file_size%once_read_size);
						crc_current = crc16((unsigned char*)read_app_data,update_app.file_size%once_read_size,crc_last);						
				}
				else
				{
						error = (UPDATE_ERROR_t)FlashRead(FLASH_APP_ADDR + i*once_read_size,read_app_data,once_read_size);
						crc_current = crc16((unsigned char*)read_app_data,once_read_size,crc_last);						
				}	
				crc_last = crc_current;
		}
		if(i == read_times)
		{
				if(crc_current == update_app.file_crc && file_crc_right_flag == 0)
				{	
					 // FlashErase(E_FLASH_SECTOR_11,1);
						FlashWrite(FLASH_PARA_ADDR,(uint8_t *)&update_app,6);
						FlashRead(FLASH_PARA_ADDR,(uint8_t *)&update_app_read,sizeof(update_app_read));		
						if(update_app_read.file_size == update_app.file_size && update_app_read.file_crc == update_app.file_crc)	
						{
								cmd_package_reply(UPDATE_APP_RESULT_FEEDBACK,ERROR_NONE);	
							  file_crc_right_flag = 1;										
						}						
				}
				else
				{
					    i = 0;
						cmd_package_reply(UPDATE_APP_RESULT_FEEDBACK,ERROR_CRC_FAILED);	
						update_app.update_result = WAIT_FOR_UPDATE;			
				}
				if(file_crc_right_flag)
				{
					if(update_app.file_read_finish_R == 0)//wait feedback from PC
					{
					    cmd_package_reply(UPDATE_APP_RESULT_FEEDBACK,ERROR_NONE);						
					}
					else
					{
						 update_app.update_result = NEED_JUMP_TO_APP;			
					}
	
				}					
		}
	}
	else
	{	
//	  FlashErase(E_FLASH_SECTOR_11,1);
		FlashRead(FLASH_PARA_ADDR,(uint8_t *)&update_app_read,sizeof(update_app_read));	
		if(update_app_read.file_size == 0xFFFFFFFF && update_app_read.file_crc == 0xFFFF)
		{
			 update_app.update_result = WAIT_FOR_UPDATE;
		}
		else
		{	
			 update_app.update_result = NEED_JUMP_TO_APP;
		}
	}

}
